﻿using System;
using Firefly.ExtensionMethods;
using Firefly.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace Firefly.Framework.Windows.Forms
{
    public class Scrollbar : Control
    {
        public event EventHandler ValueChanged;

        private bool _dragging;
        private Point _startDragPoint;
        private int _startDragValue;
        private int _max;
        private int _min;
        private int _value;
        private Rectangle _increaseRectangle;
        private Rectangle _decreaseRectangle;
        private Rectangle _barRectangle;
        private Rectangle _handRectangle;

        [ContentSerializer(Optional = true)]
        public Orientation Orientation { get; set; }
        [ContentSerializer(Optional = true)]
        public int SmallDelta { get; set; }
        [ContentSerializer(Optional = true)]
        public int LargeDelta { get; set; }
        [ContentSerializer(Optional = true)]
        public int Maximum
        {
            get { return _max; }
            set
            {
                _max = value;
                Value = Math.Min(_max, _value);
            }
        }
        [ContentSerializer(Optional = true)]
        public int Minimum
        {
            get { return _min; }
            set
            {
                _min = value;
                Value = Math.Max(_min, _value);
            }
        }
        [ContentSerializer(Optional = true)]
        public int Value
        {
            get { return _value; }
            set
            {
                int trimmedVal = Math.Max(_min, Math.Min(_max, value));

                if (_value != trimmedVal)
                {
                    _value = trimmedVal;
                    NotifyValueChanged();
                }
            }
        }

        public Scrollbar()
        {
            Orientation = Orientation.Vertical;
            _startDragPoint = Point.Zero;
            _max = 100;
            SmallDelta = 1;
            LargeDelta = 10;
            _increaseRectangle = Rectangle.Empty;
            _decreaseRectangle = Rectangle.Empty;
            _barRectangle = Rectangle.Empty;
            _handRectangle = Rectangle.Empty;
            _startDragPoint = Point.Zero;
        }

        public override void DeserializeContent(ContentReader input)
        {
            base.DeserializeContent(input);
            _max = input.ReadInt32();
            _min = input.ReadInt32();
            _value = input.ReadInt32();
        }

        protected override void OnMouseDown(MouseButtonMap map, MouseButtonMapEventArgs e)
        {
            Focus();
            Point location = InputService.Instance.MouseManager.CurrentAxisPosition.ToPoint();

            if (_decreaseRectangle.Contains(location))
            {
                Value -= SmallDelta; 
                OnMouseDown(map, e);
                return; 
            }

            if (_increaseRectangle.Contains(location))
            {
                Value += SmallDelta;
                OnMouseDown(map, e); 
                return; 
            }

            if (_handRectangle.Contains(location))
            {
                _dragging = true;
                _startDragPoint = location;
                _startDragValue = _value;
                OnMouseDown(map, e);
                return;
            }

            if (_barRectangle.Contains(location))
            {
                if (location.Y < _handRectangle.Y || location.X < _handRectangle.X)
                { 
                    Value -= LargeDelta; 
                }
                else
                { 
                    Value += LargeDelta; 
                }
            }

            OnMouseDown(map, e);
            base.OnMouseDown(map, e);
        }

        protected virtual void DoDrag() 
        {
            int position = 0;
            float ratio = 0.0f;
            Point location = InputService.Instance.MouseManager.CurrentAxisPosition.ToPoint();

            switch (Orientation)
            {
                case Orientation.Horizontal:
                    position = location.X - _barRectangle.X;
                    ratio = ((float)position / (float)_barRectangle.Width);
                    Value = _min + (int)((_max - _min) * ratio);
                    break;
                case Orientation.Vertical:
                    position = location.Y - _barRectangle.Y;
                    ratio = ((float)position / (float)_barRectangle.Height);
                    Value = _min + (int)((_max - _min) * ratio);
                    break;
            }
        }

        protected override void OnMouseScroll(MouseScrollWheelMap map, MouseScrollWheelMapEventArgs e)
        {
            Value += e.PositionDelta;
            base.OnMouseScroll(map, e);
        }

        private void NotifyValueChanged()
        {
            EventHandler handler = ValueChanged;

            if (handler != null)
            {
                handler(this, null);
            }
        }

        #region IUpdateable Members
        
        public override void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                if (_dragging)
                {
                    DoDrag();

                    if (!InputService.Instance.MouseManager.WasButtonDown(MouseButton.LeftButton))
                    {
                        _dragging = false;
                    }
                }

                base.Update(gameTime);
            }
        }

        #endregion

        #region IDrawable Members

        public override void Draw(GameTime gameTime)
        {
            if (Visible)
            {
                float ratio = 0f;
                int handlePosition = 0;

                switch (Orientation)
                {
                    case Orientation.Horizontal:
                        _decreaseRectangle = new Rectangle(DrawRectangle.X, 
                            DrawRectangle.Y,
                            WindowsService.Instance.Theme.Scrollbar.Rectangles["ScrollLeft"].Width,
                            DrawRectangle.Height);
                        _increaseRectangle = new Rectangle(DrawRectangle.X + DrawRectangle.Width - WindowsService.Instance.Theme.Scrollbar.Rectangles["ScrollRight"].Width,
                            DrawRectangle.Y,
                            WindowsService.Instance.Theme.Scrollbar.Rectangles["ScrollRight"].Width,
                            DrawRectangle.Height);
                        _barRectangle = new Rectangle(DrawRectangle.X + _decreaseRectangle.Width, 
                            DrawRectangle.Y,
                            DrawRectangle.Width - (_decreaseRectangle.Width + _increaseRectangle.Width), 
                            DrawRectangle.Height);
                        int handleW = WindowsService.Instance.Theme.Scrollbar.Rectangles["HandleH"].Width;
                        ratio = (float)_value / (_max - _min);
                        handlePosition = (int)((_barRectangle.Width - handleW) * ratio) + _barRectangle.X;
                        _handRectangle = new Rectangle(handlePosition, DrawRectangle.Y, handleW, DrawRectangle.Height);
                        WindowsService.Instance.Theme.Scrollbar.Draw("ScrollLeft", _decreaseRectangle, Color);
                        WindowsService.Instance.Theme.Scrollbar.Draw("ScrollRight", _increaseRectangle, Color);
                        WindowsService.Instance.Theme.Scrollbar.Draw("BarH", _barRectangle, Color);
                        WindowsService.Instance.Theme.Scrollbar.Draw("HandleH", _handRectangle, Color);
                        DrawChildren(gameTime);
                        break;
                    case Orientation.Vertical:
                        _decreaseRectangle = new Rectangle(DrawRectangle.X, DrawRectangle.Y, DrawRectangle.Width,
                            WindowsService.Instance.Theme.Scrollbar.Rectangles["ScrollUp"].Height);
                        _increaseRectangle = new Rectangle(DrawRectangle.X, DrawRectangle.Y + DrawRectangle.Height -
                            WindowsService.Instance.Theme.Scrollbar.Rectangles["ScrollDown"].Height, DrawRectangle.Width,
                            WindowsService.Instance.Theme.Scrollbar.Rectangles["ScrollDown"].Height);
                        _barRectangle = new Rectangle(DrawRectangle.X, DrawRectangle.Y + _decreaseRectangle.Height,
                            DrawRectangle.Width, DrawRectangle.Height - (_decreaseRectangle.Height + _increaseRectangle.Height));
                        int handleH = WindowsService.Instance.Theme.Scrollbar.Rectangles["HandleV"].Height;
                        ratio = (float)_value / (_max - _min);
                        handlePosition = (int)((_barRectangle.Height - handleH) * ratio) + _barRectangle.Y;
                        _handRectangle = new Rectangle(DrawRectangle.X, handlePosition, DrawRectangle.Width, handleH);
                        WindowsService.Instance.Theme.Scrollbar.Draw("ScrollUp", _decreaseRectangle, Color);
                        WindowsService.Instance.Theme.Scrollbar.Draw("ScrollDown", _increaseRectangle, Color);
                        WindowsService.Instance.Theme.Scrollbar.Draw("BarV", _barRectangle, Color);
                        WindowsService.Instance.Theme.Scrollbar.Draw("HandleV", _handRectangle, Color);
                        DrawChildren(gameTime);
                        break;
                }
            }
        }

        #endregion
    }
}
